package com.qys.livesMall.file.server.upload.service;

import com.qys.livesMall.common.constants.Constants;
import com.qys.livesMall.common.exception.QysException;
import com.qys.livesMall.common.utils.MessageUtils;
import com.qys.livesMall.common.utils.file.MimeTypeUtils;
import com.qys.livesMall.file.server.upload.enums.FileEnum;
import com.qys.livesMall.file.server.upload.service.impl.local.proper.LocalStorageProperties;
import com.qys.livesMall.file.server.upload.service.impl.oss.proper.OssStorageProperties;
import com.qys.livesMall.file.server.upload.utils.FilePathUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.io.*;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

/**
 * Created by IntelliJ IDEA.
 * User: dp
 * Date: 2023/05/19
 * Time: 12:02
 * DESCRIPTION: 文件上传
 */
@Slf4j
public abstract class UploadFileService {

    @Resource
    private VideoStoreService videoStoreService;

    /**
     * 文件上传
     *
     * @param file
     * @param type
     */
    public String uploadFile(MultipartFile file, String type) {
        String fileName = file.getOriginalFilename();

        //文件校验
        String extension = getExtension(file);
        checkParam(Objects.requireNonNull(fileName).length(), file.getSize(), extension);

        //获取文件名
        fileName = extractFilename(fileName);

        //获取文件对象枚举
        FileEnum fileEnum = FileEnum.getByType(type);
        if (fileEnum == null) {
            throw new QysException(MessageUtils.message("file.type.error"));
        }
        String fileDir = FilePathUtil.buildPath(fileEnum, null);

        //上传文件
        uploadFile(file, fileDir, fileName);

        //视频路径
        String fileNamePath = fileDir + fileName;

        //上传类型为视频
        if (isAllowedExtension(extension, MimeTypeUtils.VIDEO_EXTENSION)) {
            //图片地址
            String imgPath = FilePathUtil.buildPath(FileEnum.SHORT_VIDEO_IMG, null);
            //添加视频到视频库
            CompletableFuture<String> future = videoStoreService.addFileToVideoStore(fileNamePath);
            //等待视频插入完成
            CompletableFuture.allOf(future).join();
            if (future.isDone()) {
                //视频抽帧
                videoFirstFrames(file, fileNamePath, imgPath, type);
            }
        }
        //图片类型
        else {
            videoStoreService.updateImageUrlToVideoStore(null, fileNamePath, type);
        }

        return fileNamePath;
    }

    /**
     * 校验文件信息
     *
     * @param fileNameLength
     * @param fileSize
     * @param extension
     */
    private void checkParam(int fileNameLength, long fileSize, String extension) {
        if (fileNameLength > Constants.File.DEFAULT_FILE_NAME_LENGTH) {
            throw new QysException(MessageUtils.message("file.length.error"));
        }
        if (fileSize > OssStorageProperties.fileMaxSize * 1024 * 1024) {
            throw new QysException(MessageUtils.message("file.size.large"));
        }
        if (!isAllowedExtension(extension, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION)) {
            throw new QysException(MessageUtils.message("file.format.incorrect"));
        }
    }


    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    public String getExtension(MultipartFile file) {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension)) {
            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
        }
        return extension;
    }

    /**
     * 判断MIME类型是否是允许的MIME类型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 编码文件名
     */
    public String extractFilename(String fileName) {
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        return RandomStringUtils.randomAlphanumeric(20) + suffix;
    }

    /**
     * 视频抽帧
     *
     * @param videoPath 视频地址
     * @param imagePath 保存图片地址
     */
    public String saveVideoFirstFrames(String videoPath, String imagePath) {
        File video = new File(videoPath);
        if (!video.getParentFile().exists()) {
            log.error("视频文件不存在:{}", video);
        }

        Java2DFrameConverter converter = new Java2DFrameConverter();
        FFmpegFrameGrabber grabber = null;
        try {

            grabber = new FFmpegFrameGrabber(video);
            grabber.start();

            int ftp = grabber.getLengthInFrames();
            int flag = 0;
            Frame frame = null;

            while (flag <= ftp) {
                //获取帧
                frame = grabber.grabImage();
                //过滤前3帧，避免出现全黑图片
                if ((flag > 3) && (frame != null)) {
                    break;
                }
                flag++;
            }

            //创建文件夹
            checkFolder(imagePath);

            //文件名
            String imageName = extractFilename("aaa.jpg");

            //输出图片
            File imageFile = new File(LocalStorageProperties.localFilePath + imagePath + imageName);
            ImageIO.write(converter.getBufferedImage(frame), "jpg", imageFile);

            return imageName;
        } catch (Exception e) {
            log.error("获取视频中的图片出错:", e);
        } finally {
            try {
                if (grabber != null) {
                    grabber.close();
                    grabber.stop();
                }
                converter.close();
            } catch (FrameGrabber.Exception e) {
                log.error("关闭视频抽帧处理出错:", e);
            }
        }
        return null;
    }

    /**
     * 判断路径是否存在
     *
     * @param filePath 文件路径
     */
    @SuppressWarnings("ResultOfMethodCallIgnored")
    protected File checkFolder(String filePath) {
        File folder = new File(LocalStorageProperties.localFilePath + filePath);
        log.info("文件路径：{}", filePath);
        if (!folder.exists()) {
            folder.mkdirs();
        }

        return folder;
    }

    /**
     * 上传文件到本地
     *
     * @param file     文件流
     * @param filePath 文件路径
     */
    protected void uploadFileToLocal(MultipartFile file, String filePath) {
        OutputStream outputStream = null;
        InputStream inputStream = null;

        try {
            inputStream = file.getInputStream();
            outputStream = new FileOutputStream(filePath);

            // 创建一个缓冲区
            byte[] buffer = new byte[1024];
            int length;
            // 从输入流中读取数据，并将其写入输出流中
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
        } catch (Exception e) {
            log.error("文件复制到本地出错:", e);
            throw new QysException(MessageUtils.message("file.upload.error"));
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error("关闭文件流出错", e);
            }
        }
    }

    /**
     * 更新视频图片到视频库
     *
     * @param videoPath 视频地址
     * @param imgPath   图片地址
     * @param type      上传类型
     */
    protected void updateImageUrlToVideoStore(String videoPath, String imgPath, String type) {
        videoStoreService.updateImageUrlToVideoStore(videoPath, imgPath, type);
    }

    /**
     * 文件上传
     *
     * @param file     文件流
     * @param filePath 文件路径前缀
     * @param fileName 文件名
     */
    public abstract void uploadFile(MultipartFile file, String filePath, String fileName);

    /**
     * 获取文件信息
     *
     * @param fileName 文件名
     * @param response
     * @param request
     */
    public abstract void getLocalFile(String fileName, HttpServletResponse response, HttpServletRequest request);

    /**
     * 获取视频第一帧保存为图片信息
     *
     * @param file      文件流
     * @param videoPath 视频地址
     * @param imgPath   图片地址
     * @param type      上传类型
     */
    public abstract void videoFirstFrames(MultipartFile file, String videoPath, String imgPath, String type);
}
